/*******************************************************************************
 * Copyright (c) 2012-2019 Hangzhou C-SKY Microsystems Co., Ltd.
 * 
 * All rights reserved. This software is proprietary and confidential to
 * Hangzhou C-SKY Microsystems Co., Ltd. and its licensors.
 *
 * Contributors:
 *     Hangzhou C-SKY Microsystems Co., Ltd.
 *
 * 2019.6.18   Jiang Long(long_jiang@c-sky.com)
 *     Initial API and implementation
 *******************************************************************************/

/**
 * Driver for flash program.
 */
#include <string.h>
#include "hal/driver/sfud.h"
#include "syscounter.h"
/**
 * ERROR TYPE. MUST NOT BE MODIFIED 
 */
#define ERROR_INIT             -200
#define ERROR_READID           -201
#define ERROR_PROGRAM          -202
#define ERROR_READ             -203
#define ERROR_ERASE            -204
#define ERROR_CHIPERASE        -205
#define ERROR_UNINIT           -206
#define ERROR_CHECKSUM         -207
#define ERROR_FLASH_INDEX      -208


/**
 * Customize this method to perform any initialization
 * needed to access your flash device.
 *
 * @return: if this method returns an error,MUST RUTURN ERROR_INIT,
 * Otherwise return 0.
 */
int  flashInit(){
	// TODO 
	sfud_xip_dis();
	
	Drv_Init();
	
	int result  ;
	
#ifndef  __25M_QSPI
	result = sfud_init();
#endif

    return result;
}

/**
 * Customize this method to perform any un-initialization
 * needed to access your flash device.
 *
 * @return: if this method returns an error,MUST RUTURN ERROR_UNINIT,
 * Otherwise return 0.
 */
int  flashUnInit(){
	// TODO 
  sfud_xip_dis();
  return 0;
}

/**
 * Customize this method to read flash ID
 *
 * @param flashID: returns for flash ID
 *
 * @return: if this method returns an error,MUST RUTURN ERROR_READID,
 * Otherwise return 0.
 */
int  flashID(unsigned int* flashID){
	// TODO 
	sfud_xip_en();
	return 0;
}

/**
 * This method takes the data pointed to by the src parameter
 * and writes it to the flash blocks indicated by the
 * dst parameter.
 *
 * @param dst : destination address where flash program
 * @param src : address of data
 * @param length : data length
 *
 * @return : if this method returns an error,MUST RUTURN ERROR_PROGRAM,
 * Otherwise return 0.
 */
int flashProgram(char* dst, char *src, int length){
	// TODO 
	int result =0;
	
	switch((uint32_t)dst >> 20)
	{
		case 0x002:
		case 0x004: memcpy(dst, src, length);
					return 0;
		
		case 0x005: sfud_xip_dis();
					result = (0 == sfud_write((uint32_t)dst & 0x000FFFFF, length, (const uint8_t *)src))? 0:length;
//					sfud_xip_en();
					return result;
		     
		default: return ERROR_PROGRAM;
	}
}

/**
 * Customize this method to read data from a group of flash blocks into a buffer
 *
 * @param dst : reads the contents of those flash blocks into the address pointed to by
 * the dst parameter.
 * @param src : a pointer to a single flash.
 * @param length : data length
 *
 *  @return: if this method returns an error,MUST RUTURN ERROR_READ,
 * Otherwise return 0.
 */
int flashRead(char* dst, char *src, int length){
	// TODO 
	sfud_xip_en();
	
	switch((uint32_t)src >> 20)
	{
		case 0x002:
		case 0x004: 
		case 0x005: memcpy(dst, src, length);
					return 0;
		
//		case 0x005: return (sfud_read((uint32_t)src & 0x000FFFFF, length, (uint8_t *)dst) == 0) ? 0:ERROR_READ;
		     
		default: return ERROR_READ;
	}  
}

/**
 * Customize this method to erase a group of flash blocks.
 *
 * @param dst : a pointer to the base of the flash device.
 * NOTE: dst will always be sector aligned, the sector size is stored in FlashDev.c#FlashDevices#Devs#PageSize
 * @param length : erase length
 * NOTE: length will always be sector aligned, the sector size is stored in FlashDev.c#FlashDevices#Devs#PageSize
 *
 * @return : if this method returns an error,MUST RUTURN ERROR_ERASE,
 * Otherwise return 0
 */
int flashErase(char *dst, int length){
	// TODO 
	int result;
	
	switch((uint32_t)dst >> 20)
	{
		case 0x002:
		case 0x004: memset(dst, 0, length);
					return 0;
		
		case 0x005: sfud_xip_dis();
					result = (sfud_erase((uint32_t)dst & 0x000FFFFF, length) == 0) ? 0:ERROR_ERASE;
//					sfud_xip_en();
					return result;
					
		default: return ERROR_ERASE;
	} 
}

/**
 * Customize this method to erase the whole flash.
 *
 * @return : if this method returns an error,MUST RUTURN ERROR_CHIPERASE,
 * Otherwise return 0.
 */
int flashChipErase( ){
	// TODO 
	sfud_xip_dis();
	int result = sfud_chip_erase();
	syscnt_mdelay(1000);
	sfud_xip_en();
	
	return result == 0 ? 0:ERROR_CHIPERASE;
}

/**
 * Customize this method to make the veryfiy process more quickly.
 * 
 * @param dst : a pointer to the base of the flash device.
 * NOTE: dst will always be 4 aligned.
 * @param length : the lenght of the data which will be used for checksum
 * NOTE: the length will always be 4 aligned.
 * @param checksum : the expected checksum value in the programmed file(ihex,bin,elf format)
 * 
 * @return : if the specified data's checksum from dst and lenght is checksum, return 0, else return ERROR_CHECKSUM
 * @example if the flash can be read directly, you can copy following code to replace current "return 0;"
 *
  int i, sum = 0;
  for (i = 0; i < length; i++) {
   sum += dst[i];
  }
  return sum == checksum ? 0 : ERROR_CHECKSUM;
 * 
 */
int flashChecksum(char*dst, int length, int checksum) {
	int i,  sum = 0;
    
	sfud_xip_en();
#if 0	
	if(length & 0x0FFF) //如果数据长度不为整，checksum值不对
	{ return 0; }
	
	for (i = 0; i < length; i++) 
	{
		sum += dst[i];
	}
	return sum == checksum ? 0 : ERROR_CHECKSUM;
#else
	return 0;
#endif
}

/**
 * Customize this method to set flash index, indicate which flash to use
 *
 * @param index: index of flash
 *
 * @return: if this method returns an error,MUST RUTURN ERROR_FLASH_INDEX,
 * Otherwise return 0.
 */
int  flashIndex(int index){
	// TODO 
  return 0;
}

// NOTING: when debug the driver, this macro defined as 1, and then
// it must be set as 0, for release to flash programmer library
#define DEBUG_DRIVER	0

/**
 * Debug entry for driver.
 *
 * @return : if this method returns an error,MUST RUTURN ERROR_CHIPERASE,
 * Otherwise return 0.
 */
	extern int g_rwBuffer[1024];
	extern int g_dstAddress;
	extern int g_length;
	
int flashTest(){
#if DEBUG_DRIVER

	unsigned int ID;
	// read flash id
	flashID(&ID);
	
	for(ID=0;ID<512;ID++)
	{ g_rwBuffer[ID] = ~(ID+1); }
	
//	flashChipErase();
	flashErase((char*)g_dstAddress, 0x1000);
	
	flashProgram((char*)g_dstAddress, (char*)g_rwBuffer, 512);
	
	memset(g_rwBuffer, 0, 4*512);
	
	flashRead((char*)g_rwBuffer, (char*)g_dstAddress, 512);

    // other drivers test
    return ID;
#else
	sfud_xip_en();

#ifdef __25M_QSPI
	void (*entry_func)(void);
	entry_func = (void (*)(void))(0x510000);
	while(1) {entry_func();}
#endif
    
	return 0;
#endif
}

